/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.synyx.hades.dao.orm;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import org.synyx.hades.dao.GenericDao;
import org.synyx.hades.dao.query.QueryUtils;
import org.synyx.hades.domain.Page;
import org.synyx.hades.domain.PageImpl;
import org.synyx.hades.domain.Pageable;
import org.synyx.hades.domain.Sort;
import org.synyx.hades.domain.Sort.Property;
import org.synyx.hades.domain.Specification;
/**
* Default implementation of the {@link GenericDao} interface. This will offer
* you a more sophisticated interface than the plain {@link EntityManager}.
*
* @author Oliver Gierke
* @author Eberhard Wolff
* @param <T> the type of the entity to handle
* @param <PK> the type of the entity's identifier
*/
@Repository
public class GenericJpaDao<T, PK extends Serializable> extends
GenericDaoSupport<T> implements GenericDao<T, PK> {
/**
* Factory method to create {@link GenericJpaDao} instances.
*
* @param <T> the type of the entity to handle
* @param <PK> the type of the entity's identifier
* @param entityManager the {@link EntityManager} backing the DAO
* @param domainClass the domain class to handle
* @return
*/
public static <T, PK extends Serializable> GenericDao<T, PK> create(
final EntityManager entityManager, final Class<T> domainClass) {
GenericJpaDao<T, PK> dao = new GenericJpaDao<T, PK>();
dao.setEntityManager(entityManager);
dao.setDomainClass(domainClass);
dao.validate();
return dao;
}
/*
* (non-Javadoc)
*
* @see com.synyx.jpa.support.GenericDao#delete(java.lang.Object)
*/
public void delete(final T entity) {
EntityManager em = getEntityManager();
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.dao.GenericDao#delete(java.util.List)
*/
public void delete(final Collection<? extends T> entities) {
if (null == entities || entities.isEmpty()) {
return;
}
QueryUtils.applyAndBind(getDeleteAllQueryString(), entities,
getEntityManager()).executeUpdate();
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.dao.GenericDao#deleteAll()
*/
public void deleteAll() {
getEntityManager().createQuery(getDeleteAllQueryString())
.executeUpdate();
}
/*
* (non-Javadoc)
*
* @see
* com.synyx.jpa.support.GenericDao#readByPrimaryKey(java.io.Serializable)
*/
public T readByPrimaryKey(final PK primaryKey) {
Assert.notNull(primaryKey, "The given primaryKey must not be null!");
return getEntityManager().find(getDomainClass(), primaryKey);
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.dao.GenericDao#exists(java.io.Serializable)
*/
public boolean exists(final PK primaryKey) {
Assert.notNull(primaryKey, "The given primary key must not be null!");
return null != readByPrimaryKey(primaryKey);
}
/*
* (non-Javadoc)
*
* @see com.synyx.jpa.support.GenericDao#readAll()
*/
public List<T> readAll() {
return getReadAllQuery().getResultList();
}
/*
* (non-Javadoc)
*
* @see
* org.synyx.hades.dao.GenericDao#readAll(org.synyx.hades.domain.Specification
* )
*/
public List<T> readAll(Specification<T> spec) {
return getQuery(spec, null).getResultList();
}
/*
* (non-Javadoc)
*
* @see
* org.synyx.hades.dao.GenericDao#readAll(org.synyx.hades.domain.Specification
* , org.synyx.hades.domain.Pageable)
*/
public Page<T> readAll(Specification<T> spec, Pageable pageable) {
if (spec == null) {
return readAll(pageable);
}
TypedQuery<T> query = getQuery(spec, pageable);
return pageable == null ? new PageImpl<T>(query.getResultList())
: readPage(query, pageable, spec);
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.dao.GenericDao#readAll(org.synyx.hades.domain.Sort)
*/
public List<T> readAll(final Sort sort) {
String queryString =
QueryUtils.applySorting(getReadAllQueryString(), sort);
TypedQuery<T> query =
getEntityManager().createQuery(queryString, getDomainClass());
return (null == sort) ? readAll() : query.getResultList();
}
/*
* (non-Javadoc)
*
* @see
* org.synyx.hades.hades.dao.GenericDao#readAll(org.synyx.hades.hades.dao
* .Pageable)
*/
public Page<T> readAll(final Pageable pageable) {
if (null == pageable) {
return new PageImpl<T>(readAll());
}
return readPage(pageable, getReadAllQueryString());
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.hades.jpa.support.GenericDao#count()
*/
public Long count() {
return getEntityManager()
.createQuery(getCountQueryString(), Long.class)
.getSingleResult();
}
/*
* (non-Javadoc)
*
* @see
* org.synyx.hades.dao.GenericDao#count(org.synyx.hades.domain.Specification
* )
*/
public Long count(Specification<T> spec) {
return getCountQuery(spec).getSingleResult();
}
/*
* (non-Javadoc)
*
* @see com.synyx.jpa.support.GenericDao#save(java.lang.Object)
*/
public T save(final T entity) {
if (getIsNewStrategy().isNew(entity)) {
getEntityManager().persist(entity);
return entity;
} else {
return getEntityManager().merge(entity);
}
}
/*
* (non-Javadoc)
*
* @see
* org.synyx.hades.hades.jpa.support.GenericDao#saveAndFlush(org.synyx.hades
* .hades.jpa.support.Entity)
*/
public T saveAndFlush(final T entity) {
T result = save(entity);
flush();
return result;
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.dao.GenericDao#saveAll(java.util.List)
*/
public List<T> save(Collection<? extends T> entities) {
List<T> result = new ArrayList<T>();
if (entities == null) {
return result;
}
for (T entity : entities) {
result.add(save(entity));
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.synyx.hades.hades.jpa.support.GenericDao#flush()
*/
public void flush() {
getEntityManager().flush();
}
/**
* Reads a page of entities for the given JPQL query.
*
* @param pageable
* @param query
* @return a page of entities for the given JPQL query
*/
protected Page<T> readPage(final Pageable pageable, final String query) {
String queryString = QueryUtils.applySorting(query, pageable.getSort());
TypedQuery<T> jpaQuery =
getEntityManager().createQuery(queryString, getDomainClass());
return readPage(jpaQuery, pageable, null);
}
/**
* @param query
* @param spec
* @param pageable
* @return
*/
private Page<T> readPage(final TypedQuery<T> query,
final Pageable pageable, final Specification<T> spec) {
query.setFirstResult(pageable.getFirstItem());
query.setMaxResults(pageable.getPageSize());
return new PageImpl<T>(query.getResultList(), pageable, count(spec));
}
/**
* Creates a new {@link TypedQuery} from the given {@link Specification}.
*
* @param spec can be {@literal null}
* @param pageable can be {@literal null}
* @return
*/
private TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(getDomainClass());
Root<T> root = applySpecificationToCriteria(spec, query);
query.select(root);
if (pageable != null) {
query.orderBy(toOrders(pageable.getSort(), root, builder));
}
return getEntityManager().createQuery(query);
}
/**
* Creates a new count query for the given {@link Specification}.
*
* @param spec can be {@literal null}.
* @return
*/
private TypedQuery<Long> getCountQuery(Specification<T> spec) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<T> root = applySpecificationToCriteria(spec, query);
query.select(builder.count(root)).distinct(true);
return getEntityManager().createQuery(query);
}
/**
* Applies the given {@link Specification} to the given
* {@link CriteriaQuery}.
*
* @param spec can be {@literal null}
* @param query
* @return
*/
private <S> Root<T> applySpecificationToCriteria(Specification<T> spec,
CriteriaQuery<S> query) {
Assert.notNull(query);
Root<T> root = query.from(getDomainClass());
if (spec == null) {
return root;
}
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
private List<Order> toOrders(Sort sort, Root<T> root, CriteriaBuilder cb) {
List<Order> orders = new ArrayList<Order>();
if (sort == null) {
return orders;
}
for (Property property : sort) {
Expression<?> expression = root.get(property.getName());
orders.add(property.isAscending() ? cb.asc(expression) : cb
.desc(expression));
}
return orders;
}
}